/**
 ********************************************************************
 * @author roocket
 * @file    gpio.c
 * @version V0.0.0
 * @date    2024/3/22
 * @brief   This file is to indicate gpio functions. 
 * @attention Code file rules:
 * rule: file encoding use UTF8;
 * rule: max line length 120 characters;
 * rule: line separator \r\n;
 * rule: use clion auto code format tool.
 */
#include "gpio.h"

static int _validate_ports_well(int gpio)
{
    int len = -1;
    char path[MAX_LEN] = "";
    int fd = -1;

    /* append path */
    len = snprintf(path, sizeof(path), GPIO_DIR"/gpio%d", gpio);
    if (-1 == len) {
        printf("snprintf failed \n");
        return -1;
    }

    /* open */
    fd = open(path,O_RDONLY);
    if (-1 == fd) {
        printf("no such file\n");
        return -1;
    }

    /* close */
    close(fd);
    return 0;
}

/**
 * @brief set port whether export or unexport
 * @param gpio, the data from passing
 * @param file, port name
 * @return failed at -1
 */
int gpio_set_ports(struct gpio_settings *gpio, const char *file)
{
    int fd, len;
    int ret = -1;
    char buffer[MAX_LEN];
    char path[MAX_LEN] = "";

    /* append path */
    len = snprintf(path, sizeof(path), GPIO_DIR "/%s",file);
    if (len < 0) {
        printf("snprintf failed\n");
        return -1;
    }

    /* open gpio */
    fd = open(path, O_WRONLY);
    if (fd < 0)
    return fd;

    len = snprintf(buffer, sizeof(buffer), "%d", gpio->gpio);
    if (len < 0) {
        printf("snprintf failed\n");
        return -1;
    }

    printf("the path is %s, the buffer is %s, len is %d\n",path,buffer,len);
    ret = write(fd, buffer, len);
    if (-1 == ret) {
        printf("write failed %s\n",__func__);
        return -1;
    }

    close(fd);

    return 0;
}

/**
 * @brief set export
 * @param gpio, the data from passing
 * @param file, port name
 * @return failed at -1
 */
int gpio_set_export(struct gpio_settings *gpio, const char *file)
{
    if (!_validate_ports_well(gpio->gpio))
        return 0;
    return gpio_set_ports(gpio,file);
}

/**
 * @brief set unexport
 * @param gpio, the data from passing
 * @param file, port name
 * @return failed at -1
 */
int gpio_set_unexport(struct gpio_settings *gpio, const char *file)
{
    if (!_validate_ports_well(gpio->gpio) )
        return gpio_set_ports(gpio,file);
    return 0;
}

/**
 * @brief set direction for gpio
 * @param gpio, the data from passing
 * @param flag, in or out (direction)
 * @return failed at -1
 */
int gpio_set_direction(struct gpio_settings *gpio, unsigned int flag)
{
    int fd, len;
    char path[MAX_LEN];
    int ret = -1;

    len = snprintf(path, sizeof(path), GPIO_DIR "/gpio%d/direction", gpio->gpio);
    if (len < 0) {
        printf("snprintf failed\n");
        return -1;
    }

    fd = open(path, O_WRONLY);
    if (fd < 0) {
        printf("open failed\n");
        return fd;
    }

    if (flag) {
        ret = write(fd, "out", 4);
        if ( -1 == ret )
            return -1;
    } else {
        ret = write(fd, "in", 3);
        if ( -1 == ret )
        return -1;
    }

    close(fd);
    return 0;
}

/**
 * @brief set trigger edge
 * @param gpio, the data from passing
 * @param edge, trigger mode like rising,falling,none
 * @return failed at -1
 */
int gpio_set_edge_trigger(struct gpio_settings *gpio, char *edge)
{
    int fd;
    char buffer[MAX_LEN];
    int ret = -1;

    snprintf(buffer, sizeof(buffer), GPIO_DIR "/gpio%d/edge", gpio->gpio);

    fd = open(buffer, O_WRONLY);
    if (fd < 0) {
        printf("open failed\n");
        return fd;
    }

    ret = write(fd, edge, strlen(edge) + 1);
    if (-1 == ret)
        return -1;
    close(fd);
    return 0;
}

/**
 * @brief init gpio fd
 * @param gpio, the data from passing
 * @return failed at -1
 */
int gpio_inti_fd(struct gpio_settings *gpio)
{
    int fd;
    char buffer[MAX_LEN];

    snprintf(buffer, sizeof(buffer), GPIO_DIR "/gpio%d/value",gpio->gpio);

    fd = open(buffer, O_RDWR);
    if (fd < 0) {
        printf("open failed\n");
        return -1;
    }
    gpio->fd = fd;
    return 0;
}

/**
 * @brief get level
 * @param gpio, the data from passing
 * @param value, the level string
 * @return failed at -1
 */
int gpio_get_value(struct gpio_settings *gpio, char *value)
{
    int ret = -1;
    int fd = -1;

    /*
    * get value
    */
    fd = gpio->fd;
    if (fd < 0) {
        printf("get fd failed\n");
        return -1;
    }
    ret = read(fd, value, MAX_LEN);
    if (-1 == ret) {
        printf("read failed\n");
        return -1;
    }
    return 0;
}

/**
 * @brief set level
 * @param gpio, the data from passing
 * @param value, the level string
 * @return failed at -1
 */
int gpio_set_value(struct gpio_settings *gpio, unsigned int value)
{
    int ret = -1;
    int fd = -1;
    char lv[2] = "";

    if (value < 0 || value > 1)
        return -1;

    sprintf(lv, "%d", value);

    /* set value */
    fd = gpio->fd;
    if (fd < 0) {
        printf("fd failed\n");
        return -1;
    }

    ret = write(fd, lv, 2);
    if (-1 == ret) {
        printf("write failed \n");
        return -1;
    }
    return 0;
}
